/*
 * Copyright 2012 Niklas Rehfeld
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.madpie.util;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.XMLConstants;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.validation.SchemaFactory;
import org.madpie.schemas.ComponentClassType;
import org.madpie.schemas.ComponentType;
import org.madpie.schemas.Disks;
import org.madpie.schemas.Machine;
import org.madpie.schemas.ProcessorType;
import org.xml.sax.SAXException;

/**
 * TODO get/set the location of the xml schemas in an external file.
 *
 * @author Niklas Rehfeld (Niklas.Rehfeld (att) gmail.com)
 */
public class SchemaUtils
{

    /**
     * Reads a hardware xml file, and returns either the data structure
     * representing that file, or if something went wrong a new blank structure.
     *
     * @param f
     * @return
     */
    public static Machine ReadHardwareFile(File f) throws FileNotFoundException, IOException
    {
        Logger.getLogger(SchemaUtils.class.getName()).setLevel(Level.ALL);
        if (f == null || !f.canRead())
        {
            Logger.getLogger(SchemaUtils.class.getName()).log(Level.INFO,
                    "no input file - creating a new structure.");
            f.createNewFile();
            return newMachine();
        }
        Machine m;
        try
        {
//            JAXBContext jc = JAXBContext.newInstance("org.madpie.schemas");
            JAXBContext jc = JAXBContext.newInstance(Machine.class);
//            Logger.getLogger(SchemaUtils.class.getName()).log(Level.INFO,
//                    "JAXBContext knows: {0}", jc.toString());
            Unmarshaller unmarsh = jc.createUnmarshaller();
            unmarsh.setSchema(SchemaFactory.newInstance(
                    XMLConstants.W3C_XML_SCHEMA_NS_URI).newSchema(
                    new File("source/xml/hw.xsd")));

            InputStream is = new FileInputStream(f);
            Reader r = new InputStreamReader(is, "UTF-8");
            if (!r.ready())
            {
                Logger.getLogger(SchemaUtils.class.getName()).
                        log(Level.INFO,
                        "Can''t read from reader. file={0}", f.getAbsolutePath());
                return newMachine();
            }

            m = (Machine) unmarsh.unmarshal(r);
            r.close();
            return m;

        } catch (JAXBException ex)
        {
            Logger.getLogger(SchemaUtils.class.getName()).log(Level.INFO,
                    "Malformed XML File: {0} Creating a blank Structure.", f);
            Logger.getLogger(SchemaUtils.class.getName()).log(Level.INFO,
                    "Caused by: {0}", ex.getCause());
            return newMachine();
        } catch (SAXException e)
        {
            Logger.getLogger(SchemaUtils.class.getName()).log(Level.INFO,
                    "Validation failed: " + f.getPath(), e);
            return newMachine();
        }
    }

    private static Machine newMachine()
    {
        Machine m = new Machine();
        m.setName("");
        ProcessorType p = new ProcessorType();
        p.setArchitecture(" ");
        p.setName(" ");
        p.setVendor(" ");
        p.setOpModes(" ");
        p.setByteOrder(" ");
        m.setProcessor(p);

        ComponentType c = new ComponentType();
        c.setClazz(ComponentClassType.OTHER);
        c.setName(" ");
        c.setVendor(" ");
        m.getComponent().add(c);
        return m;
    }

    /**
     * Clean up a Machine structure. This removes empty components (ones without
     * names) unless there is only one, in which case it is kept as a dummy one
     * so the file will still validate..
     *
     * These empty components are sometimes added by {@link #ReadHardwareFile(java.io.File)
     * }
     *
     * @param m
     * @return
     */
    public static Machine cleanMachine(Machine m)
    {
        if (m.getComponent().size() == 1)
        {
            return m;
        }
        for (int i = 0; i < m.getComponent().size(); i++)
//        for (ComponentType c : m.getComponent())
        {
            ComponentType c = m.getComponent().get(i);
            if (c.getName() == null || c.getName().trim().isEmpty())
            {
                m.getComponent().remove(c);
            }
        }
        return m;
    }

    public static Disks readDiskFile(File f) throws JAXBException, SAXException, FileNotFoundException, IOException
    {
        Disks d;
        JAXBContext jc = JAXBContext.newInstance(Disks.class);
        Unmarshaller unmarsh = jc.createUnmarshaller();
        unmarsh.setSchema(SchemaFactory.newInstance(
                XMLConstants.W3C_XML_SCHEMA_NS_URI).newSchema(
                new File("source/xml/disks.xsd")));

        InputStream is = new FileInputStream(f);
        Reader r = null;
        try
        {
            r = new InputStreamReader(is, "UTF-8");
        } catch (UnsupportedEncodingException ex)
        {
            Logger.getLogger(SchemaUtils.class.getName()).
                    log(Level.SEVERE, null, ex);
        }
        if (!r.ready())
        {
            Logger.getLogger(SchemaUtils.class.getName()).log(Level.INFO,
                    "Can''t read from reader. file={0}", f.getAbsolutePath());
            return null;
        }

        d = (Disks) unmarsh.unmarshal(r);
        r.close();
        return d;

    }
}
